package com.huawang.mdesign.model.service.inter;

import com.huawang.business.constants.ModelConstantURI;
import com.huawang.business.dto.ModelPropertyInfo;
import com.huawang.business.service.model.base.ElementModelPropertyInfoService;
import com.huawang.business.service.model.core.display.ExecutorManager;
import com.huawang.core.definition.mof.UMLDefinitionConstants;
import com.huawang.core.definition.mof.instances.MetaClassInstance;
import com.huawang.core.semantics.entity.queryResult.MofResultContainer;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Service
public class ModelPropertyInfoService {
    private final List<String> defaultInstanceKeys = Stream.of(UMLDefinitionConstants.NamedElement_name,
                    UMLDefinitionConstants.Element_appliedStereotype,
                    UMLDefinitionConstants.Element_owner,
                    UMLDefinitionConstants.Element_constraints,
                    UMLDefinitionConstants.Element_activeHyperLink,
                    UMLDefinitionConstants.Element_image,
                    UMLDefinitionConstants.Element_ownedElement,
                    UMLDefinitionConstants.Namespace_ownedDiagram,
                    UMLDefinitionConstants.BehavioredClassifier_ownedBehavior,
                    UMLDefinitionConstants.A_appliedStereotypeInstance_owner_owner,
                    UMLDefinitionConstants.NamedElement_qualifiedName
            )
            .collect(Collectors.toList());
    @Autowired
    private ExecutorManager executorManager;
    @Autowired
    private InternalUmlService internalUmlService;
    @Autowired
    private InternalModelIconService internalModelIconService;

    @Autowired
    private ElementModelPropertyInfoService elementModelPropertyInfoService;


    /**
     * 根据instance创建模型指定属性信息对象
     *
     * @param projectId
     * @param instance
     * @return
     */
    @NotNull
    public ModelPropertyInfo createSimpleModelPropertyInfo(@NotNull final String projectId,
                                                           @NotNull final MetaClassInstance instance,
                                                           @NotNull final List<String> keys) {
        return elementModelPropertyInfoService.createSimpleModelPropertyInfo(projectId, instance, keys);
    }

    /**
     * 根据instance创建模型指定属性信息对象
     *
     * @param projectId
     * @param instances
     * @return
     */
    @NotNull
    public List<ModelPropertyInfo> createModelPropertyInfoAndTypeList(@NotNull final String projectId,
                                                                      @NotNull final List<MetaClassInstance> instances,
                                                                      @NotNull final List<String> keys) {

        return elementModelPropertyInfoService.createModelPropertyInfoAndTypeList(projectId, instances, keys);

    }

    /**
     * 根据instance创建模型指定属性信息对象
     *
     * @param projectId
     * @param instances
     * @return
     */
    @NotNull
    public List<ModelPropertyInfo> createModelPropertyInfoList(@NotNull final String projectId,
                                                               @NotNull final List<MetaClassInstance> instances,
                                                               @NotNull final List<String> keys) {
        return elementModelPropertyInfoService.createModelPropertyInfoList(projectId, instances, keys);
    }

    /**
     * 根据instance创建模型指定属性信息对象
     *
     * @param projectId
     * @param instances
     * @return
     */
    @NotNull
    public Map<String, ModelPropertyInfo> createModelPropertyInfoMap(@NotNull final String projectId,
                                                                     @NotNull final List<MetaClassInstance> instances,
                                                                     @NotNull final List<String> keys) {
        return elementModelPropertyInfoService.createModelPropertyInfoMap(projectId, instances, keys);
    }


    @NotNull
    public Map<String, List<ModelPropertyInfo>> convertInstanceToModel(@NotNull String projectId,
                                                                       @NotNull Map<String, List<MetaClassInstance>> childrenInstanceMap,
                                                                       @NotNull List<String> keys) {
        return elementModelPropertyInfoService.convertInstanceToModel(projectId, childrenInstanceMap, keys);
    }

    public Map<String, String> queryIconBatch(String projectId,
                                              @NotNull List<MetaClassInstance> allInstances) {
        return this.executorManager.executesReturnMap(
                projectId,
                allInstances,
                (computedStatuses -> this.executorManager.getQueryIconExecutor(projectId, computedStatuses)),
                String.class
        );
    }


    @NotNull
    private Map<String, ModelPropertyInfo> getModelPropertyInfoMap(
            @NotNull final String projectId,
            @NotNull final List<MetaClassInstance> instances,
            @NotNull final BiConsumer<ModelPropertyInfo, List<String>> biConsumer) {
        Map<MetaClassInstance, List<String>> modelKeyMap = new HashMap<>(instances.size());
        Map<String, ModelPropertyInfo> modelPropertyInfoMap = new HashMap<>(instances.size());
        Map<String, Set<String>> typeUriMap = internalUmlService.getGivenTypes(projectId, instances);
        instances.forEach(instance -> {
            final List<String> newPropertyKeys = new ArrayList<>(defaultInstanceKeys);
            ModelPropertyInfo info = elementModelPropertyInfoService.build(projectId, instance);
            info.setGivenTypes(typeUriMap.getOrDefault(instance.getId(), Collections.emptySet()));
            biConsumer.accept(info, newPropertyKeys);
            newPropertyKeys.addAll(generateRelationPropertyKey(info.getGivenTypes()));
            modelKeyMap.put(instance, newPropertyKeys);
            modelPropertyInfoMap.put(instance.getId(), info);
        });

        MofResultContainer result = internalUmlService.queryPropertyByModelKeyMap(projectId, modelKeyMap);
        final Map<String, Boolean> isRelationShipMap = internalUmlService.getIsRelationShipMap(projectId, instances);

        result.getMofPropertyInfoMap().forEach((key, value) -> {
            ModelPropertyInfo info = modelPropertyInfoMap.get(key);
            if (info == null) {
                return;
            }
            final String instanceId = info.getInstance().getId();
            info.setProperties(value);
            info.setModelInfo(internalUmlService.fetchModelInfoValue(projectId, info));
            info.setIsRelationShip(
                    isRelationShipMap.containsKey(instanceId) ?
                            isRelationShipMap.get(instanceId) :
                            internalUmlService.isRelationShip(projectId, info.getInstance())
            );
            info.setGivenTypes(typeUriMap.getOrDefault(instanceId, Collections.emptySet()));
            String icon;
            if (info.getModelInfo() != null) {
                icon = internalModelIconService.queryIconByModelInfo(projectId, info);
            } else {
                icon = ModelConstantURI.STEREOTYPE_ICON;
            }

            info.setIcon(icon);
            final List<MetaClassInstance> stereotypes =
                    info.queryListInstanceValue(UMLDefinitionConstants.Element_appliedStereotype);
            if (!stereotypes.isEmpty()) {
                info.setBaseStereotype(internalUmlService.queryBaseStereotype(projectId, stereotypes));
            }
        });
        return modelPropertyInfoMap;
    }


    @NotNull
    public Map<String, ModelPropertyInfo> makeFullModelPropertyInfo(@NotNull String projectId,
                                                                    @NotNull List<MetaClassInstance> instances) {
        List<Map<String, ModelPropertyInfo>> result = new ArrayList<>();
        // 第一层查询
        final Map<String, ModelPropertyInfo> modelPropertyInfoMapA = this.getModelPropertyInfoMap(
                projectId, instances,
                (info, newPropertyKeys) -> {
                    if (info.checkInstanceType(UMLDefinitionConstants.ConnectorEnd)) {
                        newPropertyKeys.add(UMLDefinitionConstants.ConnectorEnd_role);
                    } else if (info.checkInstanceType(UMLDefinitionConstants.Comment)) {
                        newPropertyKeys.add(UMLDefinitionConstants.Comment_body);
                    } else if (info.checkInstanceType(UMLDefinitionConstants.InstanceSpecification)) {
                        newPropertyKeys.add(UMLDefinitionConstants.InstanceSpecification_classifier);
                        newPropertyKeys.add(UMLDefinitionConstants.InstanceSpecification_slot);
                    } else if (info.checkInstanceType(UMLDefinitionConstants.Slot)) {
                        newPropertyKeys.add(UMLDefinitionConstants.Slot_definingFeature);
                        newPropertyKeys.add(UMLDefinitionConstants.Slot_value);
                    } else if (info.checkInstanceType(UMLDefinitionConstants.Connector)) {
                        newPropertyKeys.add(UMLDefinitionConstants.Connector_type);
                    } else if (info.checkInstanceType(UMLDefinitionConstants.Diagram)) {
                        newPropertyKeys.add(UMLDefinitionConstants.Diagram_context);
                    } else if (info.checkInstanceType(UMLDefinitionConstants.SysML_AbstractRequirement)) {
                        newPropertyKeys.add(UMLDefinitionConstants.SysML_AbstractRequirement_id);
                        newPropertyKeys.add(UMLDefinitionConstants.SysML_AbstractRequirement_text);
                        newPropertyKeys.add(UMLDefinitionConstants.Element_appliedStereotypeInstance);
                    } else if (info.checkInstanceType(UMLDefinitionConstants.UseCase)) {
                        newPropertyKeys.add(UMLDefinitionConstants.BehavioredClassifier_classifierBehavior);
                    } else if (info.checkInstanceType(UMLDefinitionConstants.CallBehaviorAction)) {
                        newPropertyKeys.add(UMLDefinitionConstants.CallBehaviorAction_behavior);
                    } else if (info.checkInstanceType(UMLDefinitionConstants.Package)) {
                        newPropertyKeys.add(UMLDefinitionConstants.Namespace_ownedDiagram);
                    } else if (info.checkInstanceType(UMLDefinitionConstants.Interaction)) {
                        newPropertyKeys.add(UMLDefinitionConstants.Namespace_ownedDiagram);
                    } else if (info.checkInstanceType(UMLDefinitionConstants.State)) {
                        newPropertyKeys.add(UMLDefinitionConstants.State_submachine);
                    } else if (info.checkInstanceType(UMLDefinitionConstants.Association)) {
                        newPropertyKeys.add(UMLDefinitionConstants.A_realization_abstraction_flow_abstraction);
                    } else if (info.checkInstanceType(UMLDefinitionConstants.Port)) {
                        newPropertyKeys.add(UMLDefinitionConstants.Port_redefinedPort);
                        newPropertyKeys.add(UMLDefinitionConstants.Property_subsettedProperty);
                    } else if (info.checkInstanceType(UMLDefinitionConstants.Property)) {
                        newPropertyKeys.addAll(Stream.of(UMLDefinitionConstants.TypedElement_type,
                                UMLDefinitionConstants.MultiplicityElement_multiplicity,
                                UMLDefinitionConstants.Property_defaultValue,
                                UMLDefinitionConstants.SysML_FlowProperty_direction,
                                UMLDefinitionConstants.Property_isDerived,
                                UMLDefinitionConstants.Property_association,
                                UMLDefinitionConstants.Property_redefinedProperty,
                                UMLDefinitionConstants.Property_subsettedProperty).collect(Collectors.toList()));
                    } else if (info.checkInstanceType(UMLDefinitionConstants.Behavior)) {
                        newPropertyKeys.add(UMLDefinitionConstants.Behavior_ownedParameter);
                    } else if (info.checkInstanceType(UMLDefinitionConstants.Operation)) {
                        newPropertyKeys.add(UMLDefinitionConstants.Operation_ownedParameter);
                    } else if (info.checkInstanceType(UMLDefinitionConstants.Reception)) {
                        newPropertyKeys.add(UMLDefinitionConstants.BehavioralFeature_ownedParameter);
                    } else if (info.checkInstanceType(UMLDefinitionConstants.AcceptEventAction)) {
                        newPropertyKeys.add(UMLDefinitionConstants.AcceptEventAction_trigger);
                    } else if (info.checkInstanceType(UMLDefinitionConstants.ConnectionPointReference)) {
                        newPropertyKeys.add(UMLDefinitionConstants.ConnectionPointReference_entry);
                        newPropertyKeys.add(UMLDefinitionConstants.ConnectionPointReference_exit);
                    } else if (info.checkInstanceType(UMLDefinitionConstants.SysML_ItemFlow)) {
                        newPropertyKeys.add(UMLDefinitionConstants.DirectedRelationship_source);
                        newPropertyKeys.add(UMLDefinitionConstants.DirectedRelationship_target);
                    } else if (info.checkInstanceType(UMLDefinitionConstants.InteractionOperand)) {
                        newPropertyKeys.add(UMLDefinitionConstants.Element_owner);
                        newPropertyKeys.add(UMLDefinitionConstants.CombinedFragment_operand);
                    }
                    newPropertyKeys.add(UMLDefinitionConstants.Element_appliedStereotype);
                }
        );
        result.add(modelPropertyInfoMapA);
        // 第二层查询
        final Map<String, ModelPropertyInfo> modelPropertyInfoMapB = this.getModelPropertyInfoMap(
                projectId,
                modelPropertyInfoMapA.values().stream()
                        .filter(it -> it.checkInstanceType(UMLDefinitionConstants.MultiplicityElement) ||
                                it.checkInstanceType(UMLDefinitionConstants.Classifier) ||
                                it.checkInstanceType(UMLDefinitionConstants.Type))
                        .map(ModelPropertyInfo::getInstance)
                        .collect(Collectors.toList()),
                (info, newPropertyKeys) -> newPropertyKeys.add(UMLDefinitionConstants.Element_appliedStereotype)
        );
        result.add(modelPropertyInfoMapB);
        final Map<String, ModelPropertyInfo> firstResult = result.get(0);
        int size = result.size();
        for (int i = 1; i < size; i++) {
            final Map<String, ModelPropertyInfo> resulti = result.get(i);
            firstResult.putAll(resulti);
        }
        return firstResult;
    }

    private Set<String> generateRelationPropertyKey(@NotNull final Set<String> givenTypes) {
        final Set<String> keys = new HashSet<>();
        if (givenTypes.contains(UMLDefinitionConstants.DirectedRelationship)) {
            if (givenTypes.contains(UMLDefinitionConstants.SysML_ItemFlow)) {
                keys.add(UMLDefinitionConstants.InformationFlow_conveyed);
            }

            keys.add(UMLDefinitionConstants.DirectedRelationship_source);
            keys.add(UMLDefinitionConstants.DirectedRelationship_target);
        } else if (givenTypes.contains(UMLDefinitionConstants.Association)) {
            keys.add(UMLDefinitionConstants.Association_endType);
            keys.add(UMLDefinitionConstants.Association_ownedEnd);
        } else if (givenTypes.contains(UMLDefinitionConstants.Transition)) {
            keys.add(UMLDefinitionConstants.Transition_source);
            keys.add(UMLDefinitionConstants.Transition_target);
        } else if (givenTypes.contains(UMLDefinitionConstants.ActivityEdge)) {
            keys.add(UMLDefinitionConstants.ActivityEdge_source);
            keys.add(UMLDefinitionConstants.ActivityEdge_target);
        } else if (givenTypes.contains(UMLDefinitionConstants.Connector)) {
            keys.add(UMLDefinitionConstants.Connector_end);
        } else if (givenTypes.contains(UMLDefinitionConstants.ExceptionHandler)) {
            keys.add(UMLDefinitionConstants.ExceptionHandler_protectedNode);
            keys.add(UMLDefinitionConstants.ExceptionHandler_handlerBody);
        }
        return keys;
    }


}
